﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using Microsoft.AnalysisServices.AdomdClient;

namespace SQX.DataModel
{
    public abstract class Query : INotifyPropertyChanged, IDisposable
    {
        private string text;
        private string header;
        private bool isSaved = false;
        protected string saveFileExt = ".txt";
        private DataTable qresult;

        public Query(string Header, string Text)
        {
            this.Header = Header;
            this.Text = Text;
        }

        public string Text
        {
            get { return text; }
            set { text = value; OnPropertyChanged("Text"); }
        }

        public DataTable QueryResult
        {
            get { return qresult; }
            set { qresult = value; OnPropertyChanged("QueryResult"); }
        }

        public string Header 
        { 
            get { return header; }
            set { header = value; OnPropertyChanged("Header"); }
        }

        public int Timeout { get; set; }

        public int LoadTimeOut { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string PropertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(PropertyName));
            }
        }

        // used to execute save function on all edited queries
        public bool IsSaved 
        { 
            get { return isSaved; }
            set { isSaved = value; OnPropertyChanged("IsSaved"); }
        }

        public abstract Task RunQueryAsync();
        public abstract Task StopQueryAsync();

        public abstract void StopQuery();

        public abstract void RunQuery();

        protected void TaskKiller(Task t)
        {
            t.Dispose();
        }

        public string SaveFileExtension { get { return saveFileExt; } }

        public Service.SourceType QueryType { get; set; }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool isDisposed)
        {
            if (isDisposed)
            {
                GC.SuppressFinalize(this);
                GC.Collect();
            }
        }
    }

    public class SqlQuery : Query
    {
        private SqlConnection connection;
        private SqlCommand cmd;

        public SqlQuery(string Header, string Text, SqlConnection Connection)
            : base(Header, Text)
        {
            this.connection = Connection;
            this.QueryType = Service.SourceType.SourceSQL;
            this.saveFileExt = ".sql";
        }

        public SqlQuery(string Header, string Text, Object Connection)
            : base(Header, Text)
        {
            this.connection = (SqlConnection)Connection;
            this.QueryType = Service.SourceType.SourceSQL;
            this.saveFileExt = ".sql";
        }

        public override Task RunQueryAsync()
        {
            Task t = new Task(RunQuery, TaskCreationOptions.LongRunning);
            t.ContinueWith(base.TaskKiller);
            t.Start();
            return t;
        }

        public override Task StopQueryAsync()
        {
            Task t = new Task(StopQuery, TaskCreationOptions.PreferFairness);
            t.ContinueWith(base.TaskKiller);
            t.Start();
            return t;
        }

        public override void StopQuery()
        {
            cmd.Cancel();
        }

        public override void RunQuery()
        {
            DataTable output = new DataTable();
            try
            {
                cmd = new SqlCommand(this.Text, this.connection);
                cmd.CommandTimeout = this.Timeout;
                cmd.CommandType = CommandType.Text;
                output.Load(cmd.ExecuteReader());
            }
            catch (Exception ex)
            {
                output.Columns.Clear();
                output.Columns.Add("Error Message");
                output.Columns.Add("Error Code");
                output.Columns.Add("Help link");
                var rw = output.NewRow();
                rw[0] = ex.Message;
                rw[1] = ex.HResult;
                rw[2] = ex.HelpLink;
                output.Rows.Add(rw);
            }

            base.QueryResult = output;
        }
    }

    public class MDXQuery : Query
    {
        private AdomdConnection connection;
        private AdomdCommand cmd;

        public MDXQuery(string Header, string Text, AdomdConnection Connection)
            : base(Header, Text)
        {
            this.connection = Connection;
            this.QueryType = Service.SourceType.SourceOLAP;
            this.saveFileExt = ".mdx";
        }

        public MDXQuery(string Header, string Text, Object Connection)
            : base(Header, Text)
        {
            this.connection = (AdomdConnection)Connection;
            this.QueryType = Service.SourceType.SourceOLAP;
            this.saveFileExt = ".mdx";
        }

        public override Task RunQueryAsync()
        {
            Task t = new Task(RunQuery, TaskCreationOptions.LongRunning);
            t.ContinueWith(base.TaskKiller);
            t.Start();
            return t;
        }

        public override Task StopQueryAsync()
        {
            Task t = new Task(StopQuery, TaskCreationOptions.PreferFairness);
            t.ContinueWith(base.TaskKiller);
            t.Start();
            return t;
        }


        public override void StopQuery()
        {
            cmd.Cancel();
        }


        public override void RunQuery()
        {
            DataTable output = new DataTable();
            try
            {
                cmd = new AdomdCommand(this.Text, connection);
                cmd.CommandTimeout = this.Timeout;

                CellSet qresult = cmd.ExecuteCellSet();

                // add load timeout break here
                if (qresult.Axes.Count > 1 && qresult.Axes[1].Positions.Count > 0)
                {
                    // get table headers
                    Position p = qresult.Axes[1].Positions[0];
                    foreach (Member m in p.Members)
                    {
                        output.Columns.Add(m.ParentLevel.Caption);
                    }

                    foreach (Position vp in qresult.Axes[1].Positions)
                    {
                        var rw = output.NewRow();
                        int i = 0;
                        foreach (Member vm in vp.Members)
                        {
                            rw[i] = vm.Caption;
                            i += 1;
                        }
                        output.Rows.Add(rw);
                    }
                }

                if (qresult.Axes[0].Positions.Count > 0)
                {
                    int j = 0;
                    int k = 0;
                    int i = output.Columns.Count;

                    foreach (Position p in qresult.Axes[0].Positions)
                    {
                        foreach (Member m in p.Members)
                        {
                            if (!output.Columns.Contains(m.Caption)) output.Columns.Add(m.Caption);
                            else output.Columns.Add(m.Caption + "1");
                        }
                    }

                    j = i;
                    if (output.Rows.Count > 0)
                    {
                        var rw = output.Rows[k];

                        foreach (Cell cl in qresult.Cells)
                        {
                            if (j < output.Columns.Count)
                            {
                                rw[j] = cl.Value;
                                j += 1;
                            }
                            else
                            {
                                k += 1;
                                j = i;
                                rw = output.Rows[k];
                                rw[j] = cl.Value;
                                j += 1;
                            }
                        }
                    }
                    else
                    {
                        var rw = output.NewRow();
                        foreach (Cell cl in qresult.Cells)
                        {
                            rw[j] = cl.Value;
                            j += 1;
                        }
                        output.Rows.Add(rw);
                    }
                }
            }

            catch (Exception ex)
            {
                output.Columns.Clear();
                output.Columns.Add("Error Message");
                output.Columns.Add("Error Code");
                output.Columns.Add("Help link");
                var rw = output.NewRow();
                rw[0] = ex.Message;
                rw[1] = ex.HResult;
                rw[2] = ex.HelpLink;
                output.Rows.Add(rw);
            }
            // add convertion
            base.QueryResult = output;
        }
    }
}
